import {
  objectToString,
  objectToStringFormat,
  singleLinkedList2
} from "../../shared";
import { AbstractList, IListConstructor } from "./list-type";
import { ListNode } from "./list-node";
/**
 * 单链表构造器
 */
export interface SingleLinkedList2Constructor<T> extends IListConstructor<T> {
  new (datas: T[]): SingleLinkedList2<T>;
  readonly [Symbol.species]: SingleLinkedList2Constructor<T>;
  readonly prototype: SingleLinkedList2<T>;
  isSingleLinkedList2<T>(obj: unknown): obj is SingleLinkedList2<T>;
  of<T>(...datas: T[]): SingleLinkedList2<T>;
  from<T>(datas: T[]): SingleLinkedList2<T>;
}
/**
 * 判断是否是单链表
 * @param obj 任意对象
 */
export function isSingleLinkedList2<T>(
  obj: unknown
): obj is SingleLinkedList2<T> {
  return obj instanceof SingleLinkedList2;
}
/**
 * 单链表，
 * 有头结点
 */
export class SingleLinkedList2<T> extends AbstractList<T> {
  static [Symbol.hasInstance](instance: unknown): boolean {
    return objectToString(instance) === objectToStringFormat(singleLinkedList2);
  }
  static from<T>(datas: T[]): SingleLinkedList2<T> {
    return new SingleLinkedList2(datas);
  }
  static of<T>(...datas: T[]): SingleLinkedList2<T> {
    return new SingleLinkedList2(datas);
  }
  static isSingleLinkedList2<T>(obj: unknown): obj is SingleLinkedList2<T> {
    return isSingleLinkedList2(obj);
  }
  static get [Symbol.species](): SingleLinkedList2Constructor<any> {
    return SingleLinkedList2;
  }
  [Symbol.toStringTag] = singleLinkedList2;
  protected _firstNode: ListNode<T> = new ListNode<T>({} as T);
  constructor(datas: T[] = []) {
    super();
    for (const el of datas) this.add(el);
  }
  add(element: T, index: number = this._size): void {
    this.checkAddRange(index);
    if (index === 0) {
      this._firstNode.next = new ListNode<T>(
        element,
        null,
        this._firstNode.next
      );
    } else {
      for (const [k, v] of this.entriesForNode())
        if (k === index - 1) {
          v.next = new ListNode<T>(element, null, v.next);
          break;
        }
    }
    this._size++;
  }
  delete(index: number): T {
    this.checkRange(index);
    let el: T = (this._firstNode.next as ListNode<T>).element;
    if (this._size === 1) {
      this.clear();
    } else {
      for (const [k, v] of this.entriesForNode())
        if (k === index - 1) {
          const next = v.next as ListNode<T>;
          el = next.element;
          v.next = next.next;
          break;
        }
      this._size--;
    }
    return el;
  }
  clear(): void {
    super.clear();
    this._firstNode = new ListNode<T>({} as T);
  }
  protected *entriesForNode(): IterableIterator<[number, ListNode<T>]> {
    let node = this._firstNode.next;
    let c = 0;
    while (c !== this._size && node !== null) {
      yield [c, node];
      node = node.next;
      c++;
    }
  }
}
